﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Pai.LiveStation.Web
{
    public class QuantitativeAnalysis
    {
        private double[] _list;

        public QuantitativeAnalysis(params double[] list)
        {
            this._list = list;
        }

        public double[] List
        {
            get { return _list; }
            set { _list = value; }
        }

        public double Mode()
        {
            try
            {
                double[] i = new double[_list.Length];
                _list.CopyTo(i, 0);
                Sort(i);
                double val_mode = i[0], help_val_mode = i[0];
                int old_counter = 0, new_counter = 0;
                int j = 0;
                for (; j <= i.Length - 1; j++)
                    if (i[j] == help_val_mode) new_counter++;
                    else if (new_counter > old_counter)
                    {
                        old_counter = new_counter;
                        new_counter = 1;
                        help_val_mode = i[j];
                        val_mode = i[j - 1];
                    }
                    else if (new_counter == old_counter)
                    {
                        val_mode = double.NaN;
                        help_val_mode = i[j];
                        new_counter = 1;
                    }
                    else
                    {
                        help_val_mode = i[j];
                        new_counter = 1;
                    }
                if (new_counter > old_counter) val_mode = i[j - 1];
                else if (new_counter == old_counter) val_mode = double.NaN;
                return val_mode;
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }
        public int Length()
        {
            return _list.Length;
        }

        public double Min()
        {
            double minimum = double.PositiveInfinity;
            for (int i = 0; i <= _list.Length - 1; i++)
                if (_list[i] < minimum) minimum = _list[i];
            return minimum;
        }

        public double Max()
        {
            double maximum = double.NegativeInfinity;
            for (int i = 0; i <= _list.Length - 1; i++)
                if (_list[i] > maximum) maximum = _list[i];
            return maximum;
        }

        public double Q1()
        {
            return Qi(0.25);
        }

        public double Q2()
        {
            return Qi(0.5);
        }

        public double Q3()
        {
            return Qi(0.75);
        }

        public double Mean()
        {
            try
            {
                double sum = 0;
                for (int i = 0; i <= _list.Length - 1; i++)
                    sum += _list[i];
                return sum / _list.Length;
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public double Range()
        {
            double minimum = Min();
            double maximum = Max();
            return (maximum - minimum);
        }

        public double IQ()
        {
            return Q3() - Q1();
        }

        public double MiddleOfRange()
        {
            double minimum = Min();
            double maximum = Max();
            return (minimum + maximum) / 2;
        }

        public double Var()
        {
            try
            {
                double s = 0;
                for (int i = 0; i <= _list.Length - 1; i++)
                    s += Math.Pow(_list[i], 2);
                return (s - _list.Length * Math.Pow(Mean(), 2)) / (_list.Length - 1);
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public double S()
        {
            return Math.Sqrt(Var());
        }

        public double YULE()
        {
            try
            {
                return ((Q3() - Q2()) - (Q2() - Q1())) / (Q3() - Q1());
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public double Z(double member)
        {
            try
            {
                if (Exist(member)) return (member - Mean()) / S();
                else return double.NaN;
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public double Cov(QuantitativeAnalysis s)
        {
            try
            {
                if (this.Length() != s.Length()) return double.NaN;
                int len = this.Length();
                double sum_mul = 0;
                for (int i = 0; i <= len - 1; i++)
                    sum_mul += (this._list[i] * s._list[i]);
                return (sum_mul - len * this.Mean() * s.Mean()) / (len - 1);
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public static double Cov(QuantitativeAnalysis s1, QuantitativeAnalysis s2)
        {
            try
            {
                if (s1.Length() != s2.Length()) return double.NaN;
                int len = s1.Length();
                double sum_mul = 0;
                for (int i = 0; i <= len - 1; i++)
                    sum_mul += (s1._list[i] * s2._list[i]);
                return (sum_mul - len * s1.Mean() * s2.Mean()) / (len - 1);
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public double R(QuantitativeAnalysis design)
        {
            try
            {
                return this.Cov(design) / (this.S() * design.S());
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public static double R(QuantitativeAnalysis design1, QuantitativeAnalysis design2)
        {
            try
            {
                return Cov(design1, design2) / (design1.S() * design2.S());
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public double A(QuantitativeAnalysis design)
        {
            try
            {
                return this.Cov(design) / (Math.Pow(design.S(), 2));
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public static double A(QuantitativeAnalysis design1, QuantitativeAnalysis design2)
        {
            try
            {
                return Cov(design1, design2) / (Math.Pow(design2.S(), 2));
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        public double B(QuantitativeAnalysis design)
        {
            return this.Mean() - this.A(design) * design.Mean();
        }

        public static double B(QuantitativeAnalysis design1, QuantitativeAnalysis design2)
        {
            return design1.Mean() - A(design1, design2) * design2.Mean();
        }

        private double Qi(double i)
        {
            try
            {
                double[] j = new double[_list.Length];
                _list.CopyTo(j, 0);
                Sort(j);
                if (Math.Ceiling(_list.Length * i) == _list.Length * i)
                    return (j[(int)(_list.Length * i - 1)] + j[(int)(_list.Length * i)]) / 2;
                else return j[((int)(Math.Ceiling(_list.Length * i))) - 1];
            }
            catch (Exception)
            {
                return double.NaN;
            }
        }

        private void Sort(double[] i)
        {
            double[] temp = new double[i.Length];
            MergeSort(i, temp, 0, i.Length - 1);
        }

        private void MergeSort(double[] source, double[] temp, int left, int right)
        {
            int mid;
            if (left < right)
            {
                mid = (left + right) / 2;
                MergeSort(source, temp, left, mid);
                MergeSort(source, temp, mid + 1, right);
                Merge(source, temp, left, mid + 1, right);
            }
        }

        private void Merge(double[] source, double[] temp, int left, int mid, int right)
        {
            int i, left_end, num_elements, tmp_pos;
            left_end = mid - 1;
            tmp_pos = left;
            num_elements = right - left + 1;
            while ((left <= left_end) && (mid <= right))
            {
                if (source[left] <= source[mid])
                {
                    temp[tmp_pos] = source[left];
                    tmp_pos++;
                    left++;
                }
                else
                {
                    temp[tmp_pos] = source[mid];
                    tmp_pos++;
                    mid++;
                }
            }
            while (left <= left_end)
            {
                temp[tmp_pos] = source[left];
                left++;
                tmp_pos++;
            }
            while (mid <= right)
            {
                temp[tmp_pos] = source[mid];
                mid++;
                tmp_pos++;
            }
            for (i = 1; i <= num_elements; i++)
            {
                source[right] = temp[right];
                right--;
            }
        }

        private bool Exist(double member)
        {
            bool is_exist = false;
            int i = 0;
            while (i <= _list.Length - 1 && !is_exist)
            {
                is_exist = (_list[i] == member);
                i++;
            }
            return is_exist;
        }
    }
}